package model.strategies.metododacruz;

import model.cubos.Adesivo;
import model.cubos.Cor;
import model.cubos.CuboMagico;
import model.cubos.CuboMagicoImpl;
import model.rotations.RotacaoCamadaDireita;
import model.rotations.RotacaoCamadaEsquerda;
import model.rotations.RotacaoCamadaInferior;
import model.rotations.RotacaoCamadaSuperior;
import model.rotations.RotacaoCuboInteiroComBaseNaCamadaEntreSuperiorInferior;
import model.rotations.RotacaoCuboInteiroComBaseNaCamadaSuperior;
import model.rotations.RotacaoFatoryException;
import model.strategies.MyStrategiesException;

/**
 * Classe que implementa o algoritmo que resolve a a segunda etapa do algoritmo
 * básico do método da cruz: Acertar os cantos da cruz de acordo com as faces
 * laterais
 * 
 * @author elton
 */
public class AlgoritmoBasicoEtapaDois extends AlgoritmoBasico {

	@Override
	public void resolver(CuboMagico cubo) throws MyStrategiesException {
		try {
			setCubo((CuboMagicoImpl) cubo);
			resolverCantoInferior();
		} catch (Exception e) {
			throw new MyStrategiesException(
					"Erro ao executar a segunda etapa do algoritmo basico de resolução do cubo mágico",
					e);
		}
	}

	/**
	 * Método que retorna os movimentos necessários para resolução da etapa
	 * Canto Inferior do Cubo à partir de uma configuração recebida
	 * juntamente com a explicação necessária
	 * 
	 * @throws RotacaoFatoryException
	 */
	private void resolverCantoInferior() throws RotacaoFatoryException {

		Adesivo[][][] matriz = cubo.getCubo();
		boolean isCantoInferiorResolvido = false;

		isCantoInferiorResolvido = isCantoInferiorResolvido(matriz);

		while (!isCantoInferiorResolvido) {
			if (isPreRequisitoEtapaUmResolvido(matriz)) {
				resolverEtapaUm(matriz);
			} else if (isEtapaUmResolvida(matriz)) {
				resolverEtapaDois(matriz);
			} else if (isEtapaDoisResolvida(matriz)) {
				resolverEtapaTres(matriz);
			} else if (isEtapaTresResolvida(matriz)) {
				resolverEtapaQuatro(matriz);
			} else if (isEtapaQuatroResolvida(matriz)) {
				resolverEtapaCinco(matriz);
			} else {
				resolverEtapaSeis(matriz);
			}
			isCantoInferiorResolvido = isEtapa6Resolvida(matriz);
		}
		return;
	}

	private boolean isEtapa6Resolvida(Adesivo[][][] matriz) {
		return matriz[5][0][0].isCor(Cor.BRANCO) && // matriz[5][0][0].getKey()
													// >= 45 &&
				matriz[5][0][2].isCor(Cor.BRANCO) && // matriz[5][0][2].getKey()
														// >= 45 &&
				matriz[5][2][0].isCor(Cor.BRANCO) && // matriz[5][2][0].getKey()
														// >= 45 &&
				matriz[5][2][2].isCor(Cor.BRANCO);
	}

	private void resolverEtapaSeis(Adesivo[][][] matriz)
			throws RotacaoFatoryException {
		setDestino(matriz[0][2][0]);
		for (int i = 0; i < 4; i++) {
			setDestino(matriz[1][0][0]);
			if (matriz[0][2][0].isCor(Cor.AZUL)) {// matriz[0][2][0].getKey()
													// <= 8){
				if (!matriz[0][1][1].isCor(Cor.AZUL)) {// matriz[0][1][1].getKey()
														// != 4){
					aplicarAlgoritmoUm();
				}
			} else if (matriz[0][2][0].isCor(Cor.VERMELHO)) {// matriz[0][2][0].getKey()
																// >= 9
																// &&
																// matriz[0][2][0].getKey()
																// <=
																// 17){
				if (!matriz[0][1][1].isCor(Cor.VERMELHO)) {// matriz[0][1][1].getKey()
					aplicarAlgoritmoUm();
				}
			} else if (matriz[0][2][0].isCor(Cor.VERDE)) {// matriz[0][2][0].getKey()
															// >= 18 &&
															// matriz[0][2][0].getKey()
															// <= 26){
				if (!matriz[0][1][1].isCor(Cor.VERDE)) {// matriz[0][1][1].getKey()
					aplicarAlgoritmoUm();
				}
			} else if (matriz[0][2][0].isCor(Cor.LARANJA)) {// matriz[0][2][0].getKey()
															// >= 27 &&
															// matriz[0][2][0].getKey()
															// <= 35){
				if (!matriz[0][1][1].isCor(Cor.LARANJA)) {// matriz[0][1][1].getKey()
					aplicarAlgoritmoUm();
				}
			}
			aplicarRotacaoNoSentidoHorario(RotacaoCuboInteiroComBaseNaCamadaSuperior.class);
		}
	}

	private void aplicarAlgoritmoUm() throws RotacaoFatoryException {
		aplicarRotacaoNoSentidoAntiHorario(RotacaoCamadaEsquerda.class);
		aplicarRotacaoNoSentidoAntiHorario(RotacaoCamadaSuperior.class);
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaEsquerda.class);
	}

	private void resolverEtapaCinco(Adesivo[][][] matriz)
			throws RotacaoFatoryException {
		// (matriz[3][2][0].getKey()
		// >= 45 &&
		// matriz[3][2][0].getKey()
		// <= 53) ){
		setDestino(matriz[0][2][0]);

		while (!matriz[0][2][0].isCor(Cor.BRANCO)) {// matriz[0][2][0].getKey()
													// < 45){//enquanto
													// não existir
													// quadrado branco
													// na posi��o
													// [0][2][0]
			aplicarRotacaoNoSentidoHorario(RotacaoCuboInteiroComBaseNaCamadaSuperior.class);
		}

		setDestino(matriz[0][2][0]);

		aplicarAlgoritmoDois();
	}

	private void aplicarAlgoritmoDois() throws RotacaoFatoryException {
		aplicarRotacaoNoSentidoAntiHorario(RotacaoCamadaEsquerda.class);
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaSuperior.class);
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaEsquerda.class);
	}

	private boolean isEtapaQuatroResolvida(Adesivo[][][] matriz) {
		return matriz[0][2][0].isCor(Cor.BRANCO) | // (matriz[0][2][0].getKey()
													// >= 45 &&
													// matriz[0][2][0].getKey()
													// <= 53) ||
				matriz[1][2][0].isCor(Cor.BRANCO) | // (matriz[1][2][0].getKey()
													// >= 45 &&
													// matriz[1][2][0].getKey()
													// <= 53) ||
				matriz[2][2][0].isCor(Cor.BRANCO) | // (matriz[2][2][0].getKey()
													// >= 45 &&
													// matriz[2][2][0].getKey()
													// <= 53) ||
				matriz[3][2][0].isCor(Cor.BRANCO);
	}

	private void resolverEtapaQuatro(Adesivo[][][] matriz)
			throws RotacaoFatoryException {
		// (matriz[3][2][2].getKey()
		// >= 45 &&
		// matriz[3][2][2].getKey()
		// <= 53) ){
		setDestino(matriz[0][2][2]);

		while (!matriz[0][2][2].isCor(Cor.BRANCO)) {// matriz[0][2][2].getKey()
													// < 45){//enquanto
													// não existir
													// quadrado branco
													// na posi��o
													// [0][2][2]

			aplicarRotacaoNoSentidoHorario(RotacaoCuboInteiroComBaseNaCamadaSuperior.class);
		}
		setDestino(matriz[0][2][2]);

		aplicarAlgoritmoTres();
	}

	private void aplicarAlgoritmoTres() throws RotacaoFatoryException {
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaDireita.class);
		aplicarRotacaoNoSentidoAntiHorario(RotacaoCamadaSuperior.class);
		aplicarRotacaoNoSentidoAntiHorario(RotacaoCamadaDireita.class);
	}

	private boolean isEtapaTresResolvida(Adesivo[][][] matriz) {
		return matriz[0][2][2].isCor(Cor.BRANCO) | // (matriz[0][2][2].getKey()
													// >= 45 &&
													// matriz[0][2][2].getKey()
													// <= 53) ||
				matriz[1][2][2].isCor(Cor.BRANCO) | // (matriz[1][2][2].getKey()
													// >= 45 &&
													// matriz[1][2][2].getKey()
													// <= 53) ||
				matriz[2][2][2].isCor(Cor.BRANCO) | // (matriz[2][2][2].getKey()
													// >= 45 &&
													// matriz[2][2][2].getKey()
													// <= 53) ||
				matriz[3][2][2].isCor(Cor.BRANCO);
	}

	private void resolverEtapaTres(Adesivo[][][] matriz)
			throws RotacaoFatoryException {
		// (matriz[4][2][2].getKey()
		// >= 45 &&
		// matriz[4][2][2].getKey()
		// <= 53) ){
		setDestino(matriz[4][2][2]);

		while (!matriz[4][2][2].isCor(Cor.BRANCO)) {// matriz[4][2][2].getKey()
													// < 45){//enquanto
													// não existir
													// quadrado branco
													// na posi��o
													// [4][2][2]
			aplicarRotacaoNoSentidoHorario(RotacaoCamadaSuperior.class);
		}

		if (matriz[5][0][2].isCor(Cor.BRANCO)) {// matriz[5][0][2].getKey()
												// >= 45 &&
												// matriz[5][0][2].getKey()
												// <= 53){
			setDestino(matriz[5][0][2]);

			while (matriz[5][0][2].isCor(Cor.BRANCO)) {// matriz[5][0][2].getKey()
				aplicarAlgoritmoCinco();
			}
		}

		setDestino(matriz[0][0][0]);

		aplicarAlgoritmoQuatro();
	}

	private void aplicarAlgoritmoQuatro() throws RotacaoFatoryException {
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaDireita.class);
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaSuperior.class, 2);
		aplicarRotacaoNoSentidoAntiHorario(RotacaoCamadaDireita.class);
	}

	private boolean isEtapaDoisResolvida(Adesivo[][][] matriz) {
		return matriz[4][0][0].isCor(Cor.BRANCO) | // (matriz[4][0][0].getKey()
													// >= 45 &&
													// matriz[4][0][0].getKey()
													// <= 53) ||
				matriz[4][0][2].isCor(Cor.BRANCO) | // (matriz[4][0][2].getKey()
													// >= 45 &&
													// matriz[4][0][2].getKey()
													// <= 53) ||
				matriz[4][2][0].isCor(Cor.BRANCO) | // (matriz[4][2][0].getKey()
													// >= 45 &&
													// matriz[4][2][0].getKey()
													// <= 53) ||
				matriz[4][2][2].isCor(Cor.BRANCO);
	}

	private void resolverEtapaDois(Adesivo[][][] matriz)
			throws RotacaoFatoryException {
		// matriz[3][0][2].getKey()
		// >= 45 &&
		// matriz[3][0][2].getKey()
		// <= 53) ){
		setDestino(matriz[3][0][2]);

		while (!matriz[3][0][2].isCor(Cor.BRANCO)) {// matriz[3][0][2].getKey()
													// < 45){enquanto
													// não for branca
			aplicarRotacaoNoSentidoHorario(RotacaoCamadaSuperior.class);
		}

		setDestino(matriz[0][0][0]);
		if (matriz[0][0][0].isCor(Cor.AZUL)) {// matriz[0][0][0].getKey()
												// >= 0 &&
												// matriz[0][0][0].getKey()
												// <= 8){
			setDestino(matriz[0][1][1]);

			while (!matriz[0][1][1].isCor(Cor.AZUL)) {// matriz[0][1][1].getKey()
														// != 4){
														// enquanto não
														// for azul
				aplicarAlgoritmoCinco();
			}

			setDestino(matriz[0][2][0]);

			aplicarAlgoritmoUm();
		} else if (matriz[0][0][0].isCor(Cor.VERMELHO)) {// matriz[0][0][0].getKey()
															// >= 9 &&
															// matriz[0][0][0].getKey()
															// <= 17){
			setDestino(matriz[0][1][1]);

			while (!matriz[0][1][1].isCor(Cor.VERMELHO)) {// matriz[0][1][1].getKey()
				aplicarAlgoritmoCinco();
			}
			setDestino(matriz[0][2][0]);

			aplicarAlgoritmoUm();
		}

		else if (matriz[0][0][0].isCor(Cor.VERDE)) {// matriz[0][0][0].getKey()
													// >= 18 &&
													// matriz[0][0][0].getKey()
													// <= 26){
			setDestino(matriz[0][1][1]);

			while (!matriz[0][1][1].isCor(Cor.VERDE)) {// matriz[0][1][1].getKey()
				aplicarAlgoritmoCinco();
			}
			setDestino(matriz[0][2][0]);

			aplicarAlgoritmoUm();
		}

		else if (matriz[0][0][0].isCor(Cor.LARANJA)) {// matriz[0][0][0].getKey()
														// >= 27 &&
														// matriz[0][0][0].getKey()
														// <= 35){
			setDestino(matriz[0][1][1]);

			while (!matriz[0][1][1].isCor(Cor.LARANJA)) {// matriz[0][1][1].getKey()
				aplicarAlgoritmoCinco();
			}
			setDestino(matriz[0][2][0]);

			aplicarAlgoritmoUm();
		}
	}

	private void aplicarAlgoritmoCinco() throws RotacaoFatoryException {
		aplicarRotacaoNoSentidoHorario(RotacaoCuboInteiroComBaseNaCamadaEntreSuperiorInferior.class);
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaInferior.class);
	}

	private boolean isEtapaUmResolvida(Adesivo[][][] matriz) {
		return matriz[0][0][2].isCor(Cor.BRANCO) | // (matriz[0][0][2].getKey()
													// >= 45 &&
													// matriz[0][0][2].getKey()
													// <= 53) |
				(matriz[1][0][2].isCor(Cor.BRANCO)) | // matriz[1][0][2].getKey()
														// >= 45 &&
														// matriz[1][0][2].getKey()
														// <= 53) |
				(matriz[2][0][2].isCor(Cor.BRANCO)) | // matriz[2][0][2].getKey()
														// >= 45 &&
														// matriz[2][0][2].getKey()
														// <= 53) |
				(matriz[3][0][2].isCor(Cor.BRANCO));
	}

	private void resolverEtapaUm(Adesivo[][][] matriz)
			throws RotacaoFatoryException {
		// matriz[3][0][0].getKey()
		// >= 45 &&
		// matriz[3][0][0].getKey()
		// <= 53) ){

		setDestino(matriz[1][0][0]);

		while (!matriz[1][0][0].isCor(Cor.BRANCO)) {// matriz[1][0][0].getKey()
													// < 45){//enquanto
													// não existir
													// quadrado brano na
													// posi��o
													// [1][0][0]
			aplicarRotacaoNoSentidoHorario(RotacaoCamadaSuperior.class);
		}

		setDestino(matriz[0][0][2]);

		if (matriz[0][0][2].isCor(Cor.AZUL)) {// matriz[0][0][2].getKey()
												// >= 0 &&
												// matriz[0][0][2].getKey()
												// <= 8){

			setDestino(matriz[0][1][1]);

			while (!matriz[0][1][1].isCor(Cor.AZUL)) {// matriz[0][1][1].getKey()
				aplicarAlgoritmoCinco();
			}
			setDestino(matriz[0][2][2]);

			aplicarAlgoritmoSeis();
		} else if (matriz[0][0][2].isCor(Cor.VERMELHO)) {// matriz[0][0][2].getKey()
															// >= 9 &&
															// matriz[0][0][2].getKey()
															// <= 17){

			setDestino(matriz[0][1][1]);

			while (!matriz[0][1][1].isCor(Cor.VERMELHO)) {// matriz[0][1][1].getKey()
				aplicarAlgoritmoCinco();
			}
			setDestino(matriz[0][2][2]);

			aplicarAlgoritmoSeis();
		}

		else if (matriz[0][0][2].isCor(Cor.VERDE)) {// matriz[0][0][2].getKey()
													// >= 18 &&
													// matriz[0][0][2].getKey()
													// <= 26){

			setDestino(matriz[0][1][1]);

			while (!matriz[0][1][1].isCor(Cor.VERDE)) {// matriz[0][1][1].getKey()
				aplicarAlgoritmoCinco();
			}
			setDestino(matriz[0][2][2]);

			aplicarAlgoritmoSeis();
		}

		else if (matriz[0][0][2].isCor(Cor.LARANJA)) {// matriz[0][0][2].getKey()
														// >= 27 &&
														// matriz[0][0][2].getKey()
														// <= 35 ){
			setDestino(matriz[0][1][1]);

			while (!matriz[0][1][1].isCor(Cor.LARANJA)) {// matriz[0][1][1].getKey()
				aplicarAlgoritmoCinco();
			}

			setDestino(matriz[0][2][2]);

			aplicarAlgoritmoSeis();
		}
	}

	private void aplicarAlgoritmoSeis() throws RotacaoFatoryException {
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaDireita.class);
		aplicarRotacaoNoSentidoHorario(RotacaoCamadaSuperior.class);
		aplicarRotacaoNoSentidoAntiHorario(RotacaoCamadaDireita.class);
	}

	private boolean isPreRequisitoEtapaUmResolvido(Adesivo[][][] matriz) {
		return matriz[0][0][0].isCor(Cor.BRANCO) | // (matriz[0][0][0].getKey()
													// >= 45 &&
													// matriz[0][0][0].getKey()
													// <= 53) |
				(matriz[1][0][0].isCor(Cor.BRANCO)) | // matriz[1][0][0].getKey()
														// >= 45 &&
														// matriz[1][0][0].getKey()
														// <= 53) |
				(matriz[2][0][0].isCor(Cor.BRANCO)) | // [2][0][0].getKey()
														// >= 45 &&
														// matriz[2][0][0].getKey()
														// <= 53) |
				(matriz[3][0][0].isCor(Cor.BRANCO));
	}

	private boolean isCantoInferiorResolvido(Adesivo[][][] matriz) {
		return matriz[5][0][0].getKey() == 45 && matriz[5][0][2].getKey() == 47
				&& matriz[5][2][0].getKey() == 51
				&& matriz[5][2][2].getKey() == 53;
	}

}
